<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2009 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace Think\Crypt\Driver;

/**
 * Xxtea 加密实现类
 */
class Xxtea {
	
	/**
	 * 加密字符串
	 * 
	 * @param string $str
	 *        	字符串
	 * @param string $key
	 *        	加密key
	 * @param integer $expire
	 *        	有效期（秒）
	 * @return string
	 */
	public static function encrypt($str, $key, $expire = 0) {
		$expire = sprintf ( '%010d', $expire ? $expire + time () : 0 );
		$str = $expire . $str;
		$v = self::str2long ( $str, true );
		$k = self::str2long ( $key, false );
		$n = count ( $v ) - 1;
		
		$z = $v [$n];
		$y = $v [0];
		$delta = 0x9E3779B9;
		$q = floor ( 6 + 52 / ($n + 1) );
		$sum = 0;
		while ( 0 < $q -- ) {
			$sum = self::int32 ( $sum + $delta );
			$e = $sum >> 2 & 3;
			for($p = 0; $p < $n; $p ++) {
				$y = $v [$p + 1];
				$mx = self::int32 ( (($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4) ) ^ self::int32 ( ($sum ^ $y) + ($k [$p & 3 ^ $e] ^ $z) );
				$z = $v [$p] = self::int32 ( $v [$p] + $mx );
			}
			$y = $v [0];
			$mx = self::int32 ( (($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4) ) ^ self::int32 ( ($sum ^ $y) + ($k [$p & 3 ^ $e] ^ $z) );
			$z = $v [$n] = self::int32 ( $v [$n] + $mx );
		}
		return self::long2str ( $v, false );
	}
	
	/**
	 * 解密字符串
	 * 
	 * @param string $str
	 *        	字符串
	 * @param string $key
	 *        	加密key
	 * @return string
	 */
	public static function decrypt($str, $key) {
		$v = self::str2long ( $str, false );
		$k = self::str2long ( $key, false );
		$n = count ( $v ) - 1;
		
		$z = $v [$n];
		$y = $v [0];
		$delta = 0x9E3779B9;
		$q = floor ( 6 + 52 / ($n + 1) );
		$sum = self::int32 ( $q * $delta );
		while ( $sum != 0 ) {
			$e = $sum >> 2 & 3;
			for($p = $n; $p > 0; $p --) {
				$z = $v [$p - 1];
				$mx = self::int32 ( (($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4) ) ^ self::int32 ( ($sum ^ $y) + ($k [$p & 3 ^ $e] ^ $z) );
				$y = $v [$p] = self::int32 ( $v [$p] - $mx );
			}
			$z = $v [$n];
			$mx = self::int32 ( (($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4) ) ^ self::int32 ( ($sum ^ $y) + ($k [$p & 3 ^ $e] ^ $z) );
			$y = $v [0] = self::int32 ( $v [0] - $mx );
			$sum = self::int32 ( $sum - $delta );
		}
		$data = self::long2str ( $v, true );
		$expire = substr ( $data, 0, 10 );
		if ($expire > 0 && $expire < time ()) {
			return '';
		}
		$data = substr ( $data, 10 );
		return $data;
	}
	private static function long2str($v, $w) {
		$len = count ( $v );
		$s = array ();
		for($i = 0; $i < $len; $i ++) {
			$s [$i] = pack ( "V", $v [$i] );
		}
		if ($w) {
			return substr ( join ( '', $s ), 0, $v [$len - 1] );
		} else {
			return join ( '', $s );
		}
	}
	private static function str2long($s, $w) {
		$v = unpack ( "V*", $s . str_repeat ( "\0", (4 - strlen ( $s ) % 4) & 3 ) );
		$v = array_values ( $v );
		if ($w) {
			$v [count ( $v )] = strlen ( $s );
		}
		return $v;
	}
	private static function int32($n) {
		while ( $n >= 2147483648 )
			$n -= 4294967296;
		while ( $n <= - 2147483649 )
			$n += 4294967296;
		return ( int ) $n;
	}
}